import axios from "axios"; // 引入 HTTP 请求库 axios
import CryptoJs = require("crypto-js"); // 引入 crypto-js，用于歌词 Base64 解码

const pageSize = 20; // 统一设置分页大小为 20

function formatMusicItem(_: any) { // 将 QQ 返回的歌曲对象格式化为统一的音乐项
  const albumid = _.albumid || (_.album || {}).id; // 兼容不同结构，获取专辑 id
  const albummid = _.albummid || (_.album || {}).mid; // 兼容不同结构，获取专辑 mid
  const albumname = _.albumname || (_.album || {}).title; // 兼容不同结构，获取专辑名
  return { // 返回统一的音乐对象
    id: _.id || _.songid, // 歌曲 id，兼容不同字段名
    songmid: _.mid || _.songmid, // QQ 歌曲 mid，用于获取 vkey
    title: _.title || _.songname, // 歌曲标题，兼容不同字段名
    artist: _.singer.map((s: any) => s.name).join(", "), // 把歌手数组的名字合并为字符串
    artwork: albummid // 专辑封面图：根据 albummid 拼接 QQ 封面地址
      ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albummid}.jpg`
      : undefined, // 若无 albummid 则不设置封面
    album: albumname, // 专辑名称
    lrc: _.lyric || undefined, // 歌词（若返回中携带）
    albumid: albumid, // 保留专辑 id，为后续接口使用
    albummid: albummid, // 保留专辑 mid，为后续接口使用
  };
}

function formatAlbumItem(_: any) { // 将 QQ 返回的专辑对象格式化为统一的专辑项
  return { // 返回统一的专辑对象
    id: _.albumID || _.albumid, // 专辑 id，兼容不同字段
    albumMID: _.albumMID || _.album_mid, // 专辑 MID，兼容不同字段
    title: _.albumName || _.album_name, // 专辑标题，兼容不同字段
    artwork: // 专辑封面图：优先使用返回的图片，否则用 albumMID 拼默认地址
      _.albumPic ||
      `https://y.gtimg.cn/music/photo_new/T002R300x300M000${
        _.albumMID || _.album_mid
      }.jpg`,
    date: _.publicTime || _.pub_time, // 专辑发布时间
    singerID: _.singerID || _.singer_id, // 歌手 id
    artist: _.singerName || _.singer_name, // 歌手名
    singerMID: _.singerMID || _.singer_mid, // 歌手 MID
    description: _.desc, // 专辑描述
  };
}

function formatArtistItem(_: any) { // 将 QQ 返回的歌手对象格式化为统一的歌手项
  return { // 返回统一的歌手对象
    name: _.singerName, // 歌手名
    id: _.singerID, // 歌手 id
    singerMID: _.singerMID, // 歌手 MID
    avatar: _.singerPic, // 头像地址
    worksNum: _.songNum, // 作品数（歌曲数）
  };
}

const searchTypeMap: Record<number, string> = { // QQ 搜索类型数字到返回体 key 的映射
  0: "song", // 歌曲
  2: "album", // 专辑
  1: "singer", // 歌手
  3: "songlist", // 歌单
  7: "lyric", // 歌词
  12: "mv", // MV
};

const headers = { // 默认请求头，用于模拟 QQ 站点环境
  referer: "https://y.qq.com", // 防盗链来源
  "user-agent": // UA，模拟浏览器环境
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
  Cookie: "uin=", // QQ 接口需要的 Cookie（此处为空）
};

const validSongFilter = (item: any) => { // 过滤付费播放的歌曲，仅保留可播放的免费歌曲
  return item.pay?.pay_play === 0 || item.pay?.payplay === 0; // pay_play 或 payplay 字段为 0 表示可播放
};

async function searchBase(query: string, page: number, type: number) { // QQ 通用搜索接口封装
  const res = ( // 发送 POST 请求到 QQ 综合搜索接口
    await axios({
      url: "https://u.y.qq.com/cgi-bin/musicu.fcg", // QQ 搜索接口地址
      method: "POST", // HTTP 方法
      data: { // 请求体
        req_1: { // 请求模块标识
          method: "DoSearchForQQMusicDesktop", // 方法名
          module: "music.search.SearchCgiService", // 模块名
          param: { // 参数
            num_per_page: pageSize, // 每页数量
            page_num: page, // 页码
            query: query, // 搜索关键词
            search_type: type, // 搜索类型（数字）
          },
        },
      },
      headers, // 请求头
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称（保持与 axios 配置习惯一致）
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 取响应数据

  return { // 返回统一的搜索结果结构
    isEnd: res.req_1.data.meta.sum <= page * pageSize, // 判断是否分页结束
    data: res.req_1.data.body[searchTypeMap[type]].list, // 按映射取对应类型的结果列表
  };
}

async function searchMusic(query: string, page: number) { // 搜索歌曲
  const songs = await searchBase(query, page, 0); // 调用基础搜索，类型 0 为歌曲
  return { // 返回统一结构
    isEnd: songs.isEnd, // 是否末页
    data: songs.data.filter(validSongFilter).map(formatMusicItem), // 过滤付费并格式化
  };
}

async function searchAlbum(query: string, page: number) { // 搜索专辑
  const albums = await searchBase(query, page, 2); // 类型 2 为专辑
  return { // 返回统一结构
    isEnd: albums.isEnd, // 是否末页
    data: albums.data.map(formatAlbumItem), // 格式化专辑项
  };
}

async function searchArtist(query: string, page: number) { // 搜索歌手
  const artists = await searchBase(query, page, 1); // 类型 1 为歌手
  return { // 返回统一结构
    isEnd: artists.isEnd, // 是否末页
    data: artists.data.map(formatArtistItem), // 格式化歌手项
  };
}

function getQueryFromUrl(key: string | null, search: string) { // 从 URL 查询串中解析参数
  try { // 捕获异常，避免空串导致报错
    const sArr = search.split("?"); // 用 ? 切分 URL 和查询部分
    let s = ""; // 初始化查询部分字符串
    if (sArr.length > 1) { // 如果存在查询部分
      s = sArr[1]; // 取出 ? 后的部分
    } else { // 不存在查询部分
      return key ? undefined : {}; // 如果指定 key，则返回 undefined，否则返回空对象
    }
    const querys = s.split("&"); // 按 & 拆分每个键值对
    const result: Record<string, string> = {}; // 存放解析结果
    querys.forEach((item) => { // 遍历每个键值对
      const temp = item.split("="); // 按 = 拆分键和值
      result[temp[0]] = decodeURIComponent(temp[1]); // 对值进行 URL 解码后存入结果
    });
    return key ? result[key] : result; // 如果传入 key，则返回指定项，否则返回整个对象
  } catch (err) { // 对异常进行兜底处理
    return key ? "" : {}; // 指定 key 返回空字符串，不指定则返回空对象
  }
}

function changeUrlQuery(obj: Record<string, any>, baseUrl: string) { // 向 URL 中追加或替换查询参数
  const query = getQueryFromUrl(null, baseUrl) as Record<string, any>; // 解析现有查询参数为对象
  let url = baseUrl.split("?")[0]; // 获取不带查询的基础 URL

  const newQuery = { ...query, ...obj }; // 合并新旧查询参数
  let queryArr: string[] = []; // 存储序列化后的查询键值对
  Object.keys(newQuery).forEach((key) => { // 遍历所有查询键
    if (newQuery[key] !== undefined && newQuery[key] !== "") { // 忽略 undefined 和空字符串
      queryArr.push(`${key}=${encodeURIComponent(newQuery[key])}`); // URL 编码值并拼接键值对
    }
  });
  return `${url}?${queryArr.join("&")}`.replace(/\?$/, ""); // 将查询拼入 URL，并去除尾部可能的 ?
}

const typeMap = { // QQ 音质类型与文件名前缀/后缀的映射
  m4a: { // 低码率 m4a
    s: "C400", // 文件名前缀
    e: ".m4a", // 扩展名
  },
  128: { // 标准 128kbps
    s: "M500", // 文件名前缀
    e: ".mp3", // 扩展名
  },
  320: { // 高品质 320kbps
    s: "M800", // 文件名前缀
    e: ".mp3", // 扩展名
  },
  ape: { // 无损 ape（部分资源可能无）
    s: "A000", // 文件名前缀
    e: ".ape", // 扩展名
  },
  flac: { // 无损 flac（常见无损格式）
    s: "F000", // 文件名前缀
    e: ".flac", // 扩展名
  },
};

function mapQualityToQqType(quality: IMusic.IQualityKey) { // 将应用的音质键映射到 QQ 的类型
  switch (quality) { // 根据音质枚举返回 QQ 类型键
    case "low": // 低音质
      return "m4a"; // 映射到 m4a
    case "standard": // 标准音质
      return "128"; // 映射到 128
    case "high": // 高音质
      return "320"; // 映射到 320
    case "super": // 超高音质（无损）
      return "flac"; // 映射到 flac
    default: // 未匹配时
      return "128"; // 默认使用 128
  }
}

async function getSourceUrl(id: string, qqType: keyof typeof typeMap = 128) { // 根据 songmid 和音质类型获取 vkey 与播放地址信息
  const mediaId = id; // 媒体 id（此处与 songmid 一样）
  const uin = ""; // QQ 用户标识（未登录情况下为空）
  const guid = (Math.random() * 10000000).toFixed(0); // 生成随机 guid，用于请求
  const typeObj = typeMap[qqType]; // 根据音质类型获取文件名前缀/后缀模板
  const file = `${typeObj.s}${id}${mediaId}${typeObj.e}`; // 拼接 vkey 请求中的文件名
  const url = changeUrlQuery( // 构造 vkey 请求 URL
    {
      "-": "getplaysongvkey", // 接口内部参数
      g_tk: 5381, // g_tk 值（常用于 QQ 接口）
      loginUin: uin, // 登录 UIN（未登录为空）
      hostUin: 0, // 主 UIN（默认 0）
      format: "json", // 返回格式 JSON
      inCharset: "utf8", // 入参编码
      outCharset: "utf-8¬ice=0", // 出参编码（原接口参数）
      platform: "yqq.json", // 平台标识
      needNewCode: 0, // 是否需要新码
      data: JSON.stringify({ // 请求体 JSON 串
        req_0: { // 请求项 req_0
          module: "vkey.GetVkeyServer", // 模块名
          method: "CgiGetVkey", // 方法名
          param: { // 参数
            filename: [file], // 文件名数组
            guid: guid, // 请求 guid
            songmid: [id], // 请求的歌曲 mid 数组
            songtype: [0], // 歌曲类型（默认 0）
            uin: uin, // 用户 UIN
            loginflag: 1, // 登录标识
            platform: "20", // 平台代码
          },
        },
        comm: { // 公共参数
          uin: uin, // 用户 UIN
          format: "json", // 返回格式
          ct: 19, // ct 参数（客户端类型）
          cv: 0, // cv 参数（版本）
          authst: "", // 认证串（此处为空）
        },
      }),
    },
    "https://u.y.qq.com/cgi-bin/musicu.fcg" // vkey 请求的目标地址
  );
  return ( // 发送 GET 请求获取 vkey 返回
    await axios({
      method: "GET", // HTTP 方法 GET
      url: url, // 请求的完整 URL
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 返回响应数据
}

async function getAlbumInfo(albumItem: IAlbum.IAlbumItem) { // 获取专辑详情（包含歌曲列表）
  const url = changeUrlQuery( // 构造专辑歌曲列表请求 URL
    {
      data: JSON.stringify({ // 请求体 JSON 串
        comm: { // 公共参数
          ct: 24, // 客户端类型
          cv: 10000, // 客户端版本
        },
        albumSonglist: { // 具体请求模块
          method: "GetAlbumSongList", // 方法名
          param: { // 参数
            albumMid: (albumItem as any).albumMID, // 专辑 MID（来源于搜索返回）
            albumID: 0, // 兼容参数（无需具体值）
            begin: 0, // 开始位置
            num: 999, // 返回数量上限（尽可能多）
            order: 2, // 排序规则
          },
          module: "music.musichallAlbum.AlbumSongList", // 模块名
        },
      }),
    },
    "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=5381&format=json&inCharset=utf8&outCharset=utf-8" // 请求地址（携带编码等参数）
  );
  const res = ( // 发送 GET 请求获取专辑歌曲列表
    await axios({
      url, // 完整 URL
      headers, // 请求头
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 响应数据
  return { // 返回包含 musicList 的对象
    musicList: res.albumSonglist.data.songList // QQ 返回的歌曲列表
      .filter((_: any) => validSongFilter(_.songInfo)) // 过滤付费歌曲
      .map((item: any) => { // 映射成统一的音乐项
        const _ = item.songInfo; // 取出 songInfo
        return formatMusicItem(_); // 格式化为音乐项
      }),
  };
}

async function getArtistSongs(artistItem: IArtist.IArtistItem, page: number) { // 获取歌手的歌曲作品（分页）
  const url = changeUrlQuery( // 构造请求 URL
    {
      data: JSON.stringify({ // 请求体 JSON 串
        comm: { // 公共参数
          ct: 24, // 客户端类型
          cv: 0, // 客户端版本
        },
        singer: { // 模块 singer
          method: "get_singer_detail_info", // 方法名：获取歌手详情（含歌曲）
          param: { // 参数
            sort: 5, // 排序参数
            singermid: (artistItem as any).singerMID, // 歌手 MID
            sin: (page - 1) * pageSize, // 起始偏移量（分页计算）
            num: pageSize, // 每页数量
          },
          module: "music.web_singer_info_svr", // 模块名
        },
      }),
    },
    "http://u.y.qq.com/cgi-bin/musicu.fcg" // 请求地址
  );

  const res = ( // 发送 GET 请求
    await axios({
      url, // 完整 URL
      method: "get", // 请求方法 GET
      headers, // 请求头
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 响应数据
  return { // 返回统一结构
    isEnd: res.singer.data.total_song <= page * pageSize, // 是否到末页（总数 <= 当前页累计）
    data: res.singer.data.songlist.filter(validSongFilter).map(formatMusicItem), // 过滤付费并格式化
  };
}

async function getArtistAlbums(artistItem: IArtist.IArtistItem, page: number) { // 获取歌手的专辑作品（分页）
  const url = changeUrlQuery( // 构造请求 URL
    {
      data: JSON.stringify({ // 请求体 JSON 串
        comm: { // 公共参数
          ct: 24, // 客户端类型
          cv: 0, // 客户端版本
        },
        singerAlbum: { // 模块 singerAlbum
          method: "get_singer_album", // 方法名：获取歌手专辑
          param: { // 参数
            singermid: (artistItem as any).singerMID, // 歌手 MID
            order: "time", // 排序按时间
            begin: (page - 1) * pageSize, // 起始偏移量
            num: pageSize / 1, // 每页数量（保持与 QQ 接口一致）
            exstatus: 1, // 状态扩展参数
          },
          module: "music.web_singer_info_svr", // 模块名
        },
      }),
    },
    "http://u.y.qq.com/cgi-bin/musicu.fcg" // 请求地址
  );
  const res = ( // 发送 GET 请求
    await axios({
      url, // 完整 URL
      method: "get", // 请求方法 GET
      headers, // 请求头
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 响应数据
  return { // 返回统一结构
    isEnd: res.singerAlbum.data.total <= page * pageSize, // 是否到末页（总数 <= 当前页累计）
    data: res.singerAlbum.data.list.map(formatAlbumItem), // 格式化专辑列表
  };
}

async function getArtistWorks<T extends Exclude<ICommon.SupportMediaType, "artist">>( // 获取歌手作品（歌曲或专辑）
  artistItem: IArtist.IArtistItem, // 歌手项
  page: number, // 页码
  type: T // 类型："music" 或 "album"
) {
  if (type === "music") { // 若请求歌曲
    return await getArtistSongs(artistItem, page); // 返回歌曲分页数据
  }
  if (type === "album") { // 若请求专辑
    return await getArtistAlbums(artistItem, page); // 返回专辑分页数据
  }
  return { isEnd: true, data: [] as any[] }; // 兜底返回空列表
}

async function getLyric(musicItem: IMusic.IMusicItem) {
  const result = ( // 发送 GET 请求到 QQ 歌词接口（JSONP）
    await axios({
      url: `http://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${
        (musicItem as any).songmid
      }&pcachetime=${new Date().getTime()}&g_tk=5381&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`, // 接口 URL，包含 songmid 和时间戳
      headers: { Referer: "https://y.qq.com", Cookie: "uin=" }, // 设置 Referer 和 Cookie
      method: "get", // HTTP 方法 GET
      xsrfCookieName: "XSRF-TOKEN", // CSRF Cookie 名称
      withCredentials: true, // 携带跨域凭证
    })
  ).data; // 响应原始数据（JSONP 字符串）

  const res = JSON.parse( // 去掉回调包装，解析为 JSON
    result.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, "") // 替换掉 JSONP 的回调名包装
  );

  return { // 返回歌词源
    rawLrc: CryptoJs.enc.Base64.parse(res.lyric).toString(CryptoJs.enc.Utf8), // QQ 歌词为 Base64 编码，需解码为 UTF-8 文本
  };
}

async function importMusicSheet(urlLike: string) {
  // 尝试解析纯数字ID、i.y.qq.com分享链接、y.qq.com歌单链接
  let id: string | undefined =
    urlLike.match(/^\s*(\d+)\s*$/)?.[1] ||
    (urlLike.match(/https?:\/\/i\.y\.qq\.com\/n2\/m\/share\/details\/taoge\.html\?.*id=([0-9]+)/) || [])[1] ||
    (urlLike.match(/https?:\/\/y\.qq\.com\/n\/ryqq\/playlist\/([0-9]+)/) || [])[1];

  // 未解析出ID则返回空数组，避免抛错
  if (!id) {
    return [];
  }

  // 请求QQ歌单详情（JSONP）
  let rawData: any;
  try {
    rawData = (
      await axios({
        url: `http://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&utf8=1&disstid=${id}&loginUin=0`,
        headers: { Referer: "https://y.qq.com/n/yqq/playlist", Cookie: "uin=" },
        method: "get",
        xsrfCookieName: "XSRF-TOKEN",
        withCredentials: true,
      })
    ).data;
  } catch {
    // 网络或服务异常时返回空数组
    return [];
  }

  // 解析JSONP并做健壮性检查
  let json: any;
  try {
    if (typeof rawData === "string") {
      // 去除任意前缀回调名与结尾右括号；兼容 callback/MusicJsonCallback/jsonCallback 等
      const cleaned = rawData.replace(/^[^(]*\(|\);?\s*$/g, "").trim();
      json = JSON.parse(cleaned);
    } else {
      json = rawData;
    }
  } catch {
    // 解析失败返回空数组
    return [];
  }

  // 安全访问cdlist与songlist，缺失则返回空数组
  const songs = json?.cdlist?.[0]?.songlist;
  if (!Array.isArray(songs)) {
    return [];
  }

  // 过滤不可播歌曲并格式化
  return songs.filter(validSongFilter).map(formatMusicItem);
}

async function getMediaSource(
  musicItem: IMusic.IMusicItem,
  quality: IMusic.IQualityKey
) {
  const qqType = mapQualityToQqType(quality); // 将质量映射到 QQ 类型
  const result = await getSourceUrl((musicItem as any).songmid, qqType as any); // 获取 vkey 数据
  let purl = ""; // 相对播放路径 purl
  let domain = ""; // 域名
  if (result.req_0 && result.req_0.data && result.req_0.data.midurlinfo) { // 如果存在 midurlinfo
    purl = result.req_0.data.midurlinfo[0].purl; // 取第一个 purl
  }
  if (domain === "") { // 如果域名为空
    domain =
      result.req_0.data.sip.find((i: string) => !i.startsWith("http://ws")) || // 选择非 ws 前缀的域名
      result.req_0.data.sip[0]; // 若找不到则取第一个
  }

  return { // 返回媒体源
    url: `${domain}${purl}`, // 拼接完整播放地址
  };
}

module.exports = { // 按插件约定导出模块（CommonJS 风格）
  platform: "QQ音乐", // 插件名称
  version: "0.0.2", // 插件版本
  author: "猫头猫", // 作者名
  srcUrl:
    "https://gitee.com/xiaocaigua/MusicFreePlugins/raw/master/dist/qq/index.js",
  cacheControl: "no-cache", // 缓存策略：不缓存
  supportedSearchType: ["music", "album", "artist"], // 支持的搜索类型
  async search(query: string, page: number, type: ICommon.SupportMediaType) { // 统一搜索入口
    if (type === "music") { // 搜索歌曲
      return await searchMusic(query, page); // 返回歌曲搜索结果
    }
    if (type === "album") { // 搜索专辑
      return await searchAlbum(query, page); // 返回专辑搜索结果
    }
    if (type === "artist") { // 搜索歌手
      return await searchArtist(query, page); // 返回歌手搜索结果
    }
  },
  getMediaSource, // 获取媒体源（播放地址）
  getLyric, // 获取歌词
  getAlbumInfo, // 获取专辑详情（歌曲列表）
  getArtistWorks, // 获取歌手作品（歌曲或专辑）
  importMusicSheet, // 导入歌单
};